#include "servicedefine.h"
#include <iostream>
#include <vector>
#include <string>
#include <grpc++/grpc++.h>
//#include "Controller.h"
#include "Utils.h"
#include "service.grpc.pb.h"
#include "../GitStoreMeta/ObjectController.h"
#include "../GitStoreMeta/DatasetController.h"
#include "../GitStoreMeta/ServiceImpl.h"


using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using namespace GitStoreGrpc;
using namespace std;

namespace gitstore {

GitStoreGrpcServiceImpl::GitStoreGrpcServiceImpl() {
    GitStoreObjectControllerPool::createInstance(std::thread::hardware_concurrency());
    GitStoreDatasetControllerPool::createInstance(std::thread::hardware_concurrency());
}

GitStoreGrpcServiceImpl::~GitStoreGrpcServiceImpl() {

}

Status GitStoreGrpcServiceImpl::CreateObject(grpc::ServerContext* context, const RequestCreateObject* request, ResponseCreateObject* response) {
    int revision_obj, revision_branch;
    auto res = RUN_SERVICE(&CObjectController::createObject, GitStoreObjectControllerPool::getInstance(), request->name(), request->note(),
                           static_cast<StoreObjectType>(request->type()), static_cast<StoreObjectType>(request->type()),
                           request->branch_name(), request->branch_note(), request->commit_note(),
                           StoreObject(request->value(), static_cast<StoreObjectType>(request->type())),
                           revision_obj, revision_branch);

    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_revision_branch(revision_branch);
        response->set_revision_object(revision_obj);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListObject(ServerContext* context, const RequestListObject* request, ResponseListObject* response) {
    
    vector<string> objects;
    auto res = RUN_SERVICE(&CObjectController::listObject, GitStoreObjectControllerPool::getInstance(), objects);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &object : objects ) {
            response->add_objects()->assign(object);
        }
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::GetObject(ServerContext* context, const RequestGetObject* request, ResponseGetObject* response) {
    Object object;
    int revision;
    auto res = RUN_SERVICE(&CObjectController::getObjectInfo, GitStoreObjectControllerPool::getInstance(), request->name(), object, revision);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        GitStoreGrpc::Object *grpc_obj = new GitStoreGrpc::Object;
        grpc_obj->set_date(object.date);
        grpc_obj->set_name(object.name);
        grpc_obj->set_note(object.note);
        grpc_obj->set_type(object.type);
        grpc_obj->set_main_branch(object.main_branch);
        response->set_allocated_object(grpc_obj);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::EditObject(ServerContext* context, const RequestEditObject* request, ResponseEditObject* response) {
    auto res = RUN_SERVICE(&CObjectController::editObjectInfo, GitStoreObjectControllerPool::getInstance(), request->name(), request->note());
    formatResponseErrorCode(response, res);
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListObjectBranches(ServerContext* context, const RequestListObjectBranches* request, ResponseListObjectBranches* response) {
    
    vector<string> vbr;
    auto res = RUN_SERVICE(&CObjectController::listObjectBranches, GitStoreObjectControllerPool::getInstance(), request->name(), vbr);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &br_name : vbr ) {
            response->add_branches(br_name);
        }
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListObjectNodes(ServerContext* context, const RequestListObjectNodes* request, ResponseListObjectNodes* response) {
    
    vector<string> vnodes;
    auto res = RUN_SERVICE(&CObjectController::listObjectVersions, GitStoreObjectControllerPool::getInstance(), request->name(), vnodes);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &node_name : vnodes ) {
            response->add_nodes(node_name);
        }
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListBranchNodes(ServerContext* context, const RequestListBranchNodes* request, ResponseListBranchNodes* response) {
    
    vector<string> vnodes;
    int revision_branch;
    auto res = RUN_SERVICE(&CObjectController::getBranchVersions, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->branch_name(), vnodes, revision_branch);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &node_name : vnodes ) {
            response->add_nodes(node_name);
        }
        response->set_revision_branch(revision_branch);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::GetValueByBranch(ServerContext* context, const RequestGetValueByBranch* request, ResponseGetValueByBranch* response) {
    
    StoreObject value;
    int revision_branch;
    ErrorStatus (CObjectController::*func) (const string&, const string&, StoreObject&, int&) = &CObjectController::getValueByBranch;
    auto res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->branch_name(), value, revision_branch);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_size(value.size());
        response->set_type(static_cast<int>(value.getType()));
        response->set_value(value.get());
        response->set_revision_branch(revision_branch);
    }
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::GetValueByVersion(grpc::ServerContext* context, const GitStoreGrpc::RequestGetValueByVersion* request, GitStoreGrpc::ResponseGetValueByVersion* response) {
    
    StoreObject value;
    ErrorStatus (CObjectController::*func) (const string&, const string&, StoreObject&) = &CObjectController::getValueByVersion;
    auto res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->version(), value);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_size(value.size());
        response->set_type(static_cast<int>(value.getType()));
        response->set_value(value.get());
    }
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::PutValue(grpc::ServerContext* context, const GitStoreGrpc::RequestPutValue* request, GitStoreGrpc::ResponsePutValue* response) {
    
    ErrorStatus res = Unknown;

    auto type = static_cast<StoreObjectType>(request->type());
    StoreObject value(type);
    switch (type) {
    case StoreObjectType::String:
        value.set(request->value());
        break;
    default:
        break;
    }
    //    auto type = static_cast<StoreObjectType>(request->type());
    int revision_new = NO_REVISION;
    res = RUN_SERVICE(&CObjectController::putValue, GitStoreObjectControllerPool::getInstance(), request->obj_name(),
                      request->branch_name(), value,
                      request->note(), request->revision_branch(), revision_new);


    //    auto res = RUN_SERVICE(&CObjectController::putValue, &op, request->obj_name(), request->branch_name(), value, request->note(), request->revision_branch(), revision_new);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_revision_branch(revision_new);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::BranchObject(ServerContext* context, const RequestBranchObject* request, ResponseBranchObject* response) {
    
    int revision_new = NO_REVISION;
    auto res = RUN_SERVICE(&CObjectController::branchObject, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->refer_branch_name(), request->new_branch_name(), request->new_branch_note(), request->revision_refer_branch(), revision_new);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_revision_new_branch(revision_new);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::GetBranch(ServerContext* context, const RequestGetBranch* request, ResponseGetBranch* response) {
    
    Branch branch;
    int revision;
    ErrorStatus (CObjectController::*func) (const string&, const string&, Branch&, int&) = &CObjectController::getBranchInfo;
    auto res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->branch_name(), branch, revision);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        GitStoreGrpc::Branch *response_branch = new GitStoreGrpc::Branch;
        response_branch->set_date(branch.date);
        response_branch->set_name(branch.name);
        response_branch->set_note(branch.note);
        response_branch->set_type(branch.type);
        response_branch->set_obj_name(branch.obj_name);
        response_branch->set_cur_version(branch.cur_version);
        response->set_allocated_branch(response_branch);
        response->set_revision_branch(revision);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::EditBranch(ServerContext* context, const RequestEditBranch* request, ResponseEditBranch* response) {
    
    auto res = RUN_SERVICE(&CObjectController::editBranchInfo, GitStoreObjectControllerPool::getInstance(),
                           request->obj_name(), request->branch_name(), request->note());
    formatResponseErrorCode(response, res);
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::GetNode(ServerContext* context, const RequestGetNode* request, ResponseGetNode* response) {
    
    Node node;
    int revision_node;
    ErrorStatus (CObjectController::*func) (const string&, const string&, Node&, int&) = &CObjectController::getNodeInfo;
    auto res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->name(),
                           request->version(), node, revision_node);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        GitStoreGrpc::Node *response_node = new GitStoreGrpc::Node;
        response_node->set_date(node.date);
        response_node->set_version(node.version);
        response_node->set_note(node.note);
        response_node->set_type(node.type);
        response_node->set_obj_name(node.obj_name);
        for ( auto &parent : node.parents ) {
            response_node->add_parents(parent);
        }
        for ( auto &child : node.children ) {
            response_node->add_children(child);
        }
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::EditNode(ServerContext* context, const RequestEditNode* request, ResponseEditNode* response) {
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::DiffObjectVersion(grpc::ServerContext* context, const GitStoreGrpc::RequestDiffObjectVersion* request, GitStoreGrpc::ResponseDiffObjectVersion* response) {
//    CObjectController op;
    Object object;
    int revision = NO_REVISION;
    auto res = RUN_SERVICE(&CObjectController::getObjectInfo, GitStoreObjectControllerPool::getInstance(), request->obj_name(), object, revision);
    if ( Success != res ) {
        formatResponseErrorCode(response, res);
        return Status::OK;
    }
    switch ( static_cast<StoreObjectType>(object.type) ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
    {
        string sres;
        auto func = &CObjectController::diffObjectVersion<std::string, const std::string&>;
        res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->version1(), request->obj_name(), request->version2(), sres, "\n");
        formatResponseErrorCode(response, res);
        if ( Success != res ) {
            formatResponseErrorCode(response, res);
            response->set_string_result(sres);
        }
    }
        break;
    case StoreObjectType::Map:
    {
        vector<string> vks;
        auto func = &CObjectController::diffObjectVersion<vector<std::string>>;
        res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->version1(), request->obj_name(), request->version2(), vks);
        formatResponseErrorCode(response, res);
        if ( Success != res ) {
            formatResponseErrorCode(response, res);
            for ( auto &key : vks ) {
                response->add_key_result(key);
            }
        }
    }
        break;
    case StoreObjectType::List:
    {
        vector<size_t> vis;
        auto func = &CObjectController::diffObjectVersion<vector<size_t>>;
        res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->version1(), request->obj_name(), request->version2(), vis);
        formatResponseErrorCode(response, res);
        if ( Success != res ) {
            formatResponseErrorCode(response, res);
            for ( auto &index : vis ) {
                response->add_index_result(index);
            }
        }
    }
        break;
    }
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::DiffObjectBranch(grpc::ServerContext* context, const GitStoreGrpc::RequestDiffObjectBranch* request, GitStoreGrpc::ResponseDiffObjectBranch* response) {
//    CObjectController op;
    Object object;
    int revision = NO_REVISION;
    auto res = RUN_SERVICE(&CObjectController::getObjectInfo, GitStoreObjectControllerPool::getInstance(), request->obj_name(), object, revision);
    if ( Success != res ) {
        formatResponseErrorCode(response, res);
        return Status::OK;
    }
    int revision_branch1 = NO_REVISION;
    int revision_branch2 = NO_REVISION;
    switch ( static_cast<StoreObjectType>(object.type) ) {
    case StoreObjectType::String:
    case StoreObjectType::Blob:
    {
        string sres;
        auto func = &CObjectController::diffObjectBranch<std::string, const std::string&>;
        res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->branch_name1(), request->obj_name(), request->branch_name2(), revision_branch1, revision_branch2, sres, "\n");
        formatResponseErrorCode(response, res);
        if ( Success != res ) {
            formatResponseErrorCode(response, res);
            response->set_string_result(sres);
        }
    }
        break;
    case StoreObjectType::Map:
    {
        vector<string> vks;
        auto func = &CObjectController::diffObjectBranch<vector<std::string>>;
        res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->branch_name1(), request->obj_name(), request->branch_name2(), revision_branch1, revision_branch2, vks);
        formatResponseErrorCode(response, res);
        if ( Success != res ) {
            formatResponseErrorCode(response, res);
            for ( auto &key : vks ) {
                response->add_key_result(key);
            }
        }
    }
        break;
    case StoreObjectType::List:
    {
        vector<size_t> vis;
        auto func = &CObjectController::diffObjectBranch<vector<size_t>>;
        res = RUN_SERVICE(func, GitStoreObjectControllerPool::getInstance(), request->obj_name(), request->branch_name1(), request->obj_name(), request->branch_name2(), revision_branch1, revision_branch2, vis);
        formatResponseErrorCode(response, res);
        if ( Success != res ) {
            formatResponseErrorCode(response, res);
            for ( auto &index : vis ) {
                response->add_index_result(index);
            }
        }
    }
        break;
    }
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::MergeVersion(grpc::ServerContext* context, const GitStoreGrpc::RequestMergeVersion* request, GitStoreGrpc::ResponseMergeVersion* response) {
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::MergeBranch(grpc::ServerContext* context, const GitStoreGrpc::RequestMergeBranch* request, GitStoreGrpc::ResponseMergeBranch* response) {
    return Status::OK;
}




Status GitStoreGrpcServiceImpl::ListDataset(ServerContext* context, const RequestListDataset* request, ResponseListDataset* response) {
    vector<string> vds;
    auto res = RUN_SERVICE(&CDatasetController::listDataset, GitStoreDatasetControllerPool::getInstance(), vds);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &ds : vds ) {
            response->add_datasets(ds);
        }
    }
    return Status::OK;
}

grpc::Status GitStoreGrpcServiceImpl::CreateDataset(grpc::ServerContext* context, const GitStoreGrpc::RequestCreateDataset* request, GitStoreGrpc::ResponseCreateDataset* response) {

    vector<string> vks;
    for ( auto &key : request->keys() ) {
        vks.emplace_back(key);
    }
    auto type = static_cast<StoreObjectType>(request->dataset_type());


    vector<StoreObject> vvs;
    vvs.reserve(request->values().size());
    for ( auto &value : request->values() ) {
        vvs.emplace_back(value, type);
    }

    int revision_ds, revision_branch;
    auto res = RUN_SERVICE(&CDatasetController::createDataset, GitStoreDatasetControllerPool::getInstance(),request->dataset_name(),
                           request->dataset_note(), request->branch_name(), request->branch_note(),
                           type, vks, vvs, revision_ds, revision_branch);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_revision_branch(revision_branch);
        response->set_revision_dataset(revision_ds);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListDatasetBranches(ServerContext* context, const RequestListDatasetBranches* request, ResponseListDatasetBranches* response) {

    vector<string> vbr;
    auto res = RUN_SERVICE(&CDatasetController::listDatasetBranches, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), vbr);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &br : vbr ) {
            response->add_branches(br);
        }
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListDatasetKeysByBranch(ServerContext* context, const RequestListDatasetKeysByBranch* request, ResponseListDatasetKeysByBranch* response) {

    vector<string> vks;
    int revision_branch;
    auto res = RUN_SERVICE(&CDatasetController::listDatasetKeysByBranch, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->branch_name(), vks, revision_branch);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &key : vks ) {
            response->add_keys(key);
        }
        response->set_revision_branch(revision_branch);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::ListDatasetKeysByVersion(ServerContext* context, const RequestListDatasetKeysByVersion* request, ResponseListDatasetKeysByVersion* response) {

    vector<string> vks;
    auto res = RUN_SERVICE(&CDatasetController::listDatasetKeysByVersion,GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->version(), vks);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &key : vks ) {
            response->add_keys(key);
        }
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::BranchDataset(ServerContext* context, const RequestBranchDataset* request, ResponseBranchDataset* response) {

    int revision_new = NO_REVISION;
    auto res = RUN_SERVICE(&CDatasetController::branchDataset, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->refer_branch_name(),
                           request->new_branch_name(),request->new_branch_note(),
                           request->revision_refer_branch(), revision_new);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_revision_new_branch(revision_new);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::PutKey(ServerContext* context, const RequestPutKey* request, ResponsePutKey* response) {


    auto type = static_cast<StoreObjectType>(request->type());
    StoreObject value(request->value(), type);
    int revision_new = NO_REVISION;
    auto res = RUN_SERVICE(&CDatasetController::putKey, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->branch_name(),
                           request->key(), value, request->revision_branch(), revision_new);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_revision_branch(revision_new);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::GetKeyByBranch(ServerContext* context, const RequestGetKeyByBranch* request, ResponseGetKeyByBranch* response) {

    StoreObject value;
    int revision_branch = NO_REVISION;
    auto res = RUN_SERVICE(&CDatasetController::getKeyByBranch, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->branch_name(), request->key(), value, revision_branch);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_size(value.size());
        response->set_type(static_cast<int>(value.getType()));
        response->set_value(value.get());
        response->set_revision_branch(revision_branch);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::GetKeyByVersion(ServerContext* context, const RequestGetKeyByVersion* request, ResponseGetKeyByVersion* response) {

    StoreObject value;
    auto res = RUN_SERVICE(&CDatasetController::getKeyByVersion, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->version(), request->key(), value);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        response->set_size(value.size());
        response->set_type(static_cast<int>(value.getType()));
        response->set_value(value.get());
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::DiffKeysByBranch(ServerContext* context, const RequestDiffKeysByBranch* request, ResponseDiffKeysByBranch* response) {

    vector<string> vks;
    int revision_branch1, revision_branch2;
    auto res = RUN_SERVICE(&CDatasetController::getDiffKeysBranch, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->branch_name1(),
                           request->branch_name2(), vks, revision_branch1, revision_branch2);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &key : vks ) {
            response->add_keys(key);
        }
        response->set_revision_branch1(revision_branch1);
        response->set_revision_branch2(revision_branch2);
    }
    return Status::OK;
}

Status GitStoreGrpcServiceImpl::DiffKeysByVersion(ServerContext* context, const RequestDiffKeysByVersion* request, ResponseDiffKeysByVersion* response) {

    vector<string> vks;
    auto res = RUN_SERVICE(&CDatasetController::getDiffKeysVersion, GitStoreDatasetControllerPool::getInstance(),
                           request->dataset_name(), request->version1(), request->version2(), vks);
    formatResponseErrorCode(response, res);
    if ( Success == res ) {
        for ( auto &key : vks ) {
            response->add_keys(key);
        }
    }
    return Status::OK;
}

}



